# -*-coding:utf-8-*-

import json
import time
import re
import random
from pyrestful import mediatypes
from pyrestful.rest import get, post, put, delete
from bson.objectid import ObjectId
from handlers.base import PayBaseHandler
from utils.auth import authenticated
from utils.sign import signed
import settings
from alidayu.topnado import Topnado
import hashlib
from utils.misc import dict_from_xml, dict_to_xml
from bson.objectid import ObjectId
from utils.http import http_post
from tornado.httpclient import AsyncHTTPClient, HTTPRequest, HTTPClient
from tornado.gen import coroutine, Return
from tornado.web import asynchronous
import logging

logger = logging.getLogger()


def sign_arguments(app, arguments, sign_field_name='sign'):
    keys = arguments.keys()
    keys.sort()
    arguments_string = ''
    for key in keys:
        value = arguments[key]
        if isinstance(value, unicode):
            value = value.encode('utf-8')
        arguments_string += '%s=%s&' % (str(key), str(value))
    arguments_string += 'key=%s' % app.get_wx_pay_sign_key()
    sign = hashlib.md5(arguments_string).hexdigest().upper()
    arguments[sign_field_name] = sign


def check_sign(app, arguments):
    sign = arguments.pop('sign')
    sign_arguments(app, arguments)
    if sign != arguments['sign']:
        raise Exception('check sign fail\n')


@coroutine
def wx_pay_http_post(app, url, arguments, **kwargs):
    # logger.debug('args before sign: %r', arguments)
    sign_arguments(app, arguments)
    # logger.debug('args after sign: %r', arguments)
    xml = dict_to_xml(arguments)
    response = yield http_post(url, xml, dump_body=False, **kwargs)
    # logger.debug(response.body)
    response = dict_from_xml(response.body)
    if 'sign' in response:
        check_sign(app, response)
    raise Return(response)


'''
- 微信内H5调起支付 -

function onBridgeReady(){
   WeixinJSBridge.invoke(
       'getBrandWCPayRequest', {
           "appId":"wx2421b1c4370ec43b",     //公众号名称，由商户传入
           "timeStamp":"1395712654",         //时间戳，自1970年以来的秒数
           "nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串
           "package":"prepay_id=u802345jgfjsdfgsdg888",
           "signType":"MD5",         //微信签名方式：
           "paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名
       },
       function(res){
           if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示：res.err_msg将在用户支付成功后返回    ok，但并不保证它绝对可靠。
       }
   );
}
if (typeof WeixinJSBridge == "undefined"){
   if( document.addEventListener ){
       document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
   }else if (document.attachEvent){
       document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
       document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
   }
}else{
   onBridgeReady();
}
'''

WX_PAY_API_PREFIX = 'https://api.mch.weixin.qq.com'


class WXPayHandler(PayBaseHandler):
    def __init__(self, application, request, **kwargs):
        super(WXPayHandler, self).__init__(application, request, **kwargs)

    @coroutine
    def update_pay_status(self, order, response):
        uid = ''  # response['attach']
        if response['return_code'] == 'SUCCESS' and \
                        response['result_code'] == 'SUCCESS' and \
                        int(response['total_fee']) == int(order.total_price * 100):
            pay_info = {
                'wx': response,
            }
            result = yield self.set_order_paid(order, paid_by='wx', pay_info=pay_info)
            raise Return(result)
        else:
            self.logger.error('check response fail:\n%s' % self.request.body)
            raise Return(False)

    '''
    Request:
    <xml>
        <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
        <attach><![CDATA[支付测试]]></attach>
        <bank_type><![CDATA[CFT]]></bank_type>
        <fee_type><![CDATA[CNY]]></fee_type>
        <is_subscribe><![CDATA[Y]]></is_subscribe>
        <mch_id><![CDATA[10000100]]></mch_id>
        <nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str>
        <openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid>
        <out_trade_no><![CDATA[1409811653]]></out_trade_no>
        <result_code><![CDATA[SUCCESS]]></result_code>
        <return_code><![CDATA[SUCCESS]]></return_code>
        <sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign>
        <sub_mch_id><![CDATA[10000100]]></sub_mch_id>
        <time_end><![CDATA[20140903131540]]></time_end>
        <total_fee>1</total_fee>
        <trade_type><![CDATA[JSAPI]]></trade_type>
        <transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id>
    </xml>

    Response:
    <xml>
        <return_code>![CDATA[SUCCESS]]</return_code>
        <return_msg>![CDATA[OK]]</return_msg>
    </xml>
    '''

    @post(_path='/platform/api/orders/{oid}/wx/pay/on_notify', _types=[str])
    @coroutine
    def wx_pay_on_notify(self, oid):
        """
        注意：同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。
        推荐的做法是，当收到通知进行处理时，首先检查对应业务数据的状态，判断该通知是否已经处理过，如果没有处理过再进行处理，如果处理过直接返回结果成功。
        在对业务数据进行状态检查和处理之前，要采用数据锁进行并发控制，以避免函数重入造成的数据混乱。
        特别提醒：商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致，防止数据泄漏导致出现“假通知”，造成资金损失。
        """
        order = yield self.orders_dao.get_record_by_id(oid)
        if order is None:
            raise Exception('invalid order id')
        self.logger.info(self.request.body)
        if 'to_be_paid' == order.status:
            response = dict_from_xml(self.request.body)
            self.logger.info(json.dumps(response))
            app = yield self.apps_dao.get_app_by_wx_mp_app_id(response['appid'])
            check_sign(app, response)
            # H5 支付时不回传 openid
            # user = yield self.users_dao.get_user_by_wx_mp_openid(app, response['openid'])
            # NOTE:
            # set self.app here because API does not authenticated or signed
            # the following update flow would use this member variable
            self.app = app
            # self.user_id = user.id_str
            self.user_id = response['attach']
            yield self.update_pay_status(order, response)
        # result = '<xml><return_code>![CDATA[SUCCESS]]</return_code><return_msg>![CDATA[OK]]</return_msg></xml>'
        result = '<xml><return_code>![CDATA[SUCCESS]]</return_code></xml>'
        self.finish(result)

    '''
    Request:
    <xml>
        <appid>wx2421b1c4370ec43b</appid>
        <attach>支付测试</attach>
        <body>JSAPI支付测试</body>
        <mch_id>10000100</mch_id>
        <detail><![CDATA[{ "goods_detail":[ { "goods_id":"iphone6s_16G", "wxpay_goods_id":"1001", "goods_name":"iPhone6s 16G", "quantity":1, "price":528800, "goods_category":"123456", "body":"苹果手机" }, { "goods_id":"iphone6s_32G", "wxpay_goods_id":"1002", "goods_name":"iPhone6s 32G", "quantity":1, "price":608800, "goods_category":"123789", "body":"苹果手机" } ] }]]></detail>
        <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
        <notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
        <openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
        <out_trade_no>1415659990</out_trade_no>
        <spbill_create_ip>14.23.150.211</spbill_create_ip>
        <total_fee>1</total_fee>
        <trade_type>JSAPI</trade_type>
        <sign>0CB01533B8C1EF103065174F50BCA001</sign>
    </xml>

    Response:
    <xml>
        <return_code><![CDATA[SUCCESS]]></return_code>
        <return_msg><![CDATA[OK]]></return_msg>
        <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
        <mch_id><![CDATA[10000100]]></mch_id>
        <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
        <openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
        <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
        <result_code><![CDATA[SUCCESS]]></result_code>
        <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
        <trade_type><![CDATA[JSAPI]]></trade_type>
    </xml>
    '''

    @post(_path='/test/api/orders/{oid}/wx/pay/prepay', types=[str])
    @coroutine
    def test_create_prepay(self, oid):
        self.logger.debug(self.request.body)
        request = dict_from_xml(self.request.body)
        app = yield self.apps_dao.get_app_by_wx_mp_app_id(request['appid'])
        check_sign(app, request)

        notify_req = {
            'return_code': 'SUCCESS',
            'appid': request['appid'],
            'mch_id': request['mch_id'],
            'nonce_str': str(ObjectId()),
            'result_code': 'SUCCESS',
            'trade_type': request['trade_type'],
            'total_fee': request['total_fee'],
            #'openid': request['openid'],
            'out_trade_no': request['out_trade_no'],
            'attach': request['attach'],
        }
        if 'openid' in request:
            notify_req['openid'] = request['openid']
        sign_arguments(app, notify_req)
        notify_req_xml = dict_to_xml(notify_req)

        http_request = HTTPRequest(request['notify_url'], method='POST', body=notify_req_xml)
        http_client = AsyncHTTPClient()
        yield http_client.fetch(http_request, raise_error=False)

        response = {
            'return_code': 'SUCCESS',
            'return_msg': 'OK',
            'appid': request['appid'],
            'mch_id': request['mch_id'],
            'nonce_str': str(ObjectId()),
            'result_code': 'SUCCESS',
            'prepay_id': str(ObjectId()),
            'trade_type': request['trade_type'],
        }
        sign_arguments(app, response)
        xml = dict_to_xml(response)
        self.finish(xml)

    @get(_path='/platform/api/orders/{oid}/wx/pay/prepay', types=[str])
    @authenticated
    @coroutine
    def get_wx_pay_prepay(self, oid):
        trade_type = self.get_argument('trade_type', 'JSAPI')
        trade_type = str(trade_type)
        if trade_type not in ['JSAPI', 'MWEB']:
            raise Exception('unsupport trade type: %s' % trade_type)
        order = yield self.orders_dao.get_record_by_id(oid)
        if order is None:
            raise Exception('invalid order id')

        if 'to_be_paid' != order.status:
            raise Exception('order is paid')

        # NOTE: body, spbill_create_ip 等这个字段微信后台好像并没有进一步判断，使用

        notify_url = '%s://%s/platform/api/orders/%s/wx/pay/on_notify' % (
            self.request.protocol, self.request.host, oid)

        self.logger.info('self.request.remote_ip: %s' % self.request.remote_ip)
        # self.logger.debug('user_id: %s, openid: %s', self.user_id, self.user.wx_mp_openid)

        arguments = {
            'appid': self.app.get_wx_mp_app_id(),
            'mch_id': self.app.get_wx_pay_mch_id(),
            'device_info': 'WEB',
            # 'body': 'DGK智能平台-数码家电',
            'body': '微信支付',
            'out_trade_no': oid,
            # 'out_trade_no': str(ObjectId()),
            'total_fee': int(order.total_price * 100),
            'spbill_create_ip': self.request.remote_ip,
            'notify_url': notify_url,
            'trade_type': trade_type,
            'nonce_str': str(ObjectId()),
            'attach': self.user.id_str,
        }
        if 'JSAPI' == trade_type:
            arguments['openid'] = self.user.wx_mp_openid
        elif 'MWEB' == trade_type:
            scene_info = {
                'h5_info': {
                    'type': 'Wap',
                    'wap_url': '',
                    'wap_name': '',
                }
            }
            arguments['scene_info'] = json.dumps(scene_info)
        if self.prod:
            if self.app.is_wx_pay_use_sandbox():
                wx_pay_api_unifiedorder_url = '%s/sandboxnew/pay/unifiedorder' % WX_PAY_API_PREFIX
            else:
                wx_pay_api_unifiedorder_url = '%s/pay/unifiedorder' % WX_PAY_API_PREFIX
        else:
            wx_pay_api_unifiedorder_url = '%s://%s/test/api/orders/%s/wx/pay/prepay' % (
                self.request.protocol, self.request.host, oid)
        # self.logger.info(wx_pay_api_unifiedorder_url)
        response = yield wx_pay_http_post(self.app, wx_pay_api_unifiedorder_url, arguments)
        self.logger.info(json.dumps(response))
        if response.has_key('prepay_id'):
            if 'mweb_url' in response:
                self.redirect(response['mweb_url'])
            else:
                result = {
                    'appId': response['appid'],
                    'timeStamp': str(int(time.time())),
                    'nonceStr': str(ObjectId()),
                    'package': 'prepay_id=%s' % response['prepay_id'],
                    'signType': 'MD5',
                }
                sign_arguments(self.app, result, sign_field_name='paySign')
                self.logger.info(json.dumps(result))
                self.respond_success(result)
        else:
            self.respond_error('invalid_request', json.dumps(response))

    '''
    Request:
    <xml>
        <appid>wx2421b1c4370ec43b</appid>
        <mch_id>10000100</mch_id>
        <nonce_str>ec2316275641faa3aacf3cc599e8730f</nonce_str>
        <transaction_id>1008450740201411110005820873</transaction_id>
        <sign>FDD167FAA73459FD921B144BAF4F4CA2</sign>
    </xml>

    Response:
    <xml>
        <return_code><![CDATA[SUCCESS]]></return_code>
        <return_msg><![CDATA[OK]]></return_msg>
        <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
        <mch_id><![CDATA[10000100]]></mch_id>
        <device_info><![CDATA[1000]]></device_info>
        <nonce_str><![CDATA[TN55wO9Pba5yENl8]]></nonce_str>
        <sign><![CDATA[BDF0099C15FF7BC6B1585FBB110AB635]]></sign>
        <result_code><![CDATA[SUCCESS]]></result_code>
        <openid><![CDATA[oUpF8uN95-Ptaags6E_roPHg7AG0]]></openid>
        <is_subscribe><![CDATA[Y]]></is_subscribe>
        <trade_type><![CDATA[MICROPAY]]></trade_type>
        <bank_type><![CDATA[CCB_DEBIT]]></bank_type>
        <total_fee>1</total_fee>
        <fee_type><![CDATA[CNY]]></fee_type>
        <transaction_id><![CDATA[1008450740201411110005820873]]></transaction_id>
        <out_trade_no><![CDATA[1415757673]]></out_trade_no>
        <attach><![CDATA[订单额外描述]]></attach>
        <time_end><![CDATA[20141111170043]]></time_end>
        <trade_state><![CDATA[SUCCESS]]></trade_state>
    </xml>
    '''

    @get(_path='/platform/api/orders/{oid}/wx/pay/status', _types=[str], _produces=mediatypes.APPLICATION_JSON)
    @authenticated
    @coroutine
    def get_wx_pay_status(self, oid):
        order = yield self.orders_dao.get_record_by_id(oid)
        if order is None:
            raise Exception('invalid order id')
        status = 'paid'
        if 'to_be_paid' == order.status:
            arguments = {
                'appid': self.app.get_wx_mp_app_id(),
                'mch_id': self.app.get_wx_pay_mch_id(),
                'out_trade_no': oid,
                'nonce_str': str(ObjectId()),
            }
            if self.app.is_wx_pay_use_sandbox():
                wx_pay_api_orderquery_url = '%s/sandboxnew/pay/orderquery' % WX_PAY_API_PREFIX
            else:
                wx_pay_api_orderquery_url = '%s/pay/orderquery' % WX_PAY_API_PREFIX
            response = yield wx_pay_http_post(self.app, wx_pay_api_orderquery_url, arguments)
            self.logger.info(json.dumps(response))
            result = yield self.update_pay_status(order, response)
            if not result:
                status = 'to_be_paid'
        self.respond_success({'status': status})

    @get(_path='/tmp/api/wx/pay/sandbox/getsignkey')
    @authenticated
    def wx_pay_sandbox_get_sign_key(self):
        # self.app = self.apps_dao.get_app('CF859FF521D73AA88EAD95197BC26420')
        arguments = {
            'mch_id': self.app.get_wx_pay_mch_id(),
            'nonce_str': str(ObjectId()),
        }
        sign_arguments(self.app, arguments)
        xml = dict_to_xml(arguments)
        url = '%s/sandboxnew/pay/getsignkey' % WX_PAY_API_PREFIX
        response = http_post(url, xml, dump_body=False)
        self.respond_success(response.body)

    @staticmethod
    @coroutine
    def wx_pay_transfers(authenticated_handler, transfer):
        app = authenticated_handler.app
        user = authenticated_handler.user
        remote_ip = authenticated_handler.request.remote_ip
        arguments = {
            'mch_appid': app.get_wx_mp_app_id(),
            'mchid': app.get_wx_pay_mch_id(),
            'openid': user.get_wx_mp_openid(),
            'nonce_str': str(ObjectId()),
            'partner_trade_no': transfer.id_str,
            'check_name': 'NO_CHECK',
            'amount': int(transfer.amount * 100),
            'desc': transfer.desc,
            'spbill_create_ip': remote_ip,
        }
        wx_pay_api_transfers_url = '%s/mmpaymkttransfers/promotion/transfers' % WX_PAY_API_PREFIX
        ca_certs = app.get_wx_pay_ca_certs()
        client_cert = app.get_wx_pay_client_cert()
        client_key = app.get_wx_pay_client_key()
        response = yield wx_pay_http_post(app, wx_pay_api_transfers_url, arguments, ca_certs=ca_certs,
                                          client_cert=client_cert, client_key=client_key)
        raise Return(response)
